<?php

class AuthException extends Exception {}

/**
 * Description of Authenticate
 *
 * @author Martin Grossmann <snake.aas@gmail.com>
 * @version 1.0
 * @package includes 
 */
class Authentication {
  
  const PERMANENT_LOGIN = true;
  const TEMPORARY_LOGIN = false;

  const DELIMITER = ":";
  
  private $lifeTime = 2678400; //31 days in seconds
    
  private $db;
  private $Cookie;
  
  private static $tokenName = "gall_persist";

  public function __construct(Doctrine\ORM\EntityManager $db) {
   /* if ($loginPermanently) {
      session_set_cookie_params ($this->lifeTime);
    } else {
      session_set_cookie_params(0);
    }  */
    
   $this->db = $db;
   $this->Cookie = new Cookie();
    
   session_start();

  }
  
  public function whoIsLogged() {
    $token = false;
    //var_dump($_SESSION);
    if (isset($_SESSION[self::$tokenName]) && $_SESSION[self::$tokenName] != '') {      
      $token = $_SESSION[self::$tokenName];      
    } else
      if ($this->Cookie->gall_persist != null && $this->Cookie->gall_persist != '') {        
        $token = $this->Cookie->gall_persist;      
        $_SESSION[self::$tokenName] = $token;      
      }
    
    if ($token) {
      //$login = new Login();
      $login = $this->db->find('Login', $token);
      
      if($login == null) {
        $this->unsetStorages();
        return null;
      }
      /*
      if (session_id() != $login->SessionID) {
        $this->unsetStorages();
        return null;
      }*/
      
      return $login->idUser;
      
    } else return null;
        
  }
  
  public function logIn($loginName, $password, $permanent = Authentication::TEMPORARY_LOGIN) {    
    
    $loginName = strtolower($loginName);
    
    $password = globalFunc::hashPassword($password, $loginName);
    $user = $this->db->getRepository('User')->findOneBy(array('eMail' => $loginName, 'password' => $password, 'isActive' => true));
       
    if ($user === null) {
      throw new AuthException (T_('Incorrect credentials or account is not activated'));        
      return;
    }  
    
    session_regenerate_id(true);
    $token = globalFunc::generateToken();
    
    $persistMe = false;
    $login = $this->db->getRepository('Login')->findOneByidUser($user->idUser);
    
    if ($login === null) {
      $login = new Login();
      $persistMe = true;
    }

    $user->isRecovery = false;
    $user->Token = null;
    $user->LastAction = new DateTime('now');
    
    $login->idUser = $user->idUser;
    $login->Token = $token;
    //$login->SessionID = session_id();
    
    if ($persistMe) $this->db->persist($login);
    $this->db->flush();
    
    $this->setStorages($token, $permanent ? $this->lifeTime : null);
    
  }
  
  public function logOut() {

    $login = null;
    if(isset($_SESSION[self::$tokenName]) && $_SESSION[self::$tokenName] != '')
      $login = $this->db->find('Login', $_SESSION[self::$tokenName]);
    
    if ($login !== null){
      $this->db->remove($login);
      $this->db->flush();
    }
    
    $this->unsetStorages();

  }
  
 public function activateAccount ($idUser, $token) {
   
   if($idUser === null || $token === null) {
     throw new AuthException(T_("Activation data missing"));
   }
   
   $user = new User();
   $user = $this->db->find('User', $idUser);
      
   
   if ($user === null) {
     throw new AuthException(T_("No such user"));
   }
   
   if ($user->isActive === true) {
     throw new AuthException(T_("Cannot activate account. Already activated"));
   }
   
   if ($user->Token != $token) {
     throw new AuthException(T_("Cannot activate account. Token mismatch"));
   }
   
   $expire = new DateTime('-1 day');
   if( $expire > $user->LastAction ) {
     throw new AuthException(T_("Cannot activate account. Time for activation is up"));
   }
     
  $user->Token = null;
  $user->isActive = true;
  $user->isRecovery = false;
  
  $this->db->flush();
   
   //if ($user->LastAction->diff(new DateTime('now'), $absolute))
   
 }

public function changePassword($oldPassword, $repeatPassword, $newPassword, User &$user) {
  if ($repeatPassword != $newPassword) throw new AuthException(T_('Entered passwords aren\'t the same'));
  
  if (globalFunc::hashPassword($oldPassword, $user->EMail) != $user->Password) throw new AuthException(T_('Old password doesn\'t match'));
  
  $this->checkPasswordPolicy($newPassword);
  
  $user->Password = globalFunc::hashPassword($newPassword, $user->EMail);
  
  $this->db->flush();
} 


public function recoverAccount($idUser, $token) {
  
   if($idUser == null || $token == null) {
     throw new AuthException(T_("Recovery data missing"));
   }
   
   //$user = new User();
   $user = $this->db->find('User', $idUser);
      
   
   if ($user === null) {
     throw new AuthException(T_("No such user"));
   }
   
   if ($user->isRecovery === false) {
     throw new AuthException(T_("Cannot recover account."));
   }
   
   if ($user->Token != $token) {
     throw new AuthException(T_("Cannot recover account. Token mismatch"));
   }
   
   $expire = new DateTime('-1 day');
   if( $expire > $user->LastAction ) {
     throw new AuthException(T_("Cannot recover account. Time for recover is up"));
   }
     
  $user->Token = null;
  $user->isActive = true;
  $user->isRecovery = false;
  
  $pass = globalFunc::generateToken();
  $user->Password = globalFunc::hashPassword($pass, $user->EMail);
  
  $this->db->flush();
  
  $this->logIn($user->EMail, $pass);
  return $pass;
} 
 

 public function recoveryRequest($email) {
   global $config;
   
   //$user = new User();
   
   try {
     $email = Validations::validateString($email, 30, Validations::regexp_EMail);
   } catch (Exception $ex) {
     throw new AuthException(T_('Entered value is not a valid e-mail address'));
   }  
   
   $user = $this->db->getRepository('User')->findOneByEMail(strtolower($email));
   
   if ($user == null) throw new AuthException(T_('Entered email is not registered'));
   
   $user->isRecovery = true;
   $user->Token = globalFunc::generateToken();
   $user->LastAction = new DateTime('now');
   
   $this->db->flush();
   
   try {
     $mail = new PHPMailer(true);   
     
     $mail->CharSet  = "UTF-8";   
     $mail->From     = $config['Mail']['From'];
     $mail->FromName = $config['Mail']['From'];
     $mail->Subject  = ProjectName." - ".T_('Password Recovery');
      
       
     $activationLink = globalFunc::getUrl().'recover.php?user='.$user->idUser.'&token='.$user->Token;
       
     $text = T_('Hi, 

You or someone else requested a new password to account with e-mail address %s on %s < %s >

To complete the password recovery process, you have to visit following address in next 24 hours

%s

Then you will be able to change your password.');       
     $mail->Body = sprintf($text, $email, ProjectName, globalFunc::getUrl(), $activationLink);
              
     $mail->AddAddress($email);     
     $mail->Send();
     
   } catch (phpmailerException $ex) {
     throw new AuthException($ex->getMessage());
   }  
   
 }
 
 public function checkPasswordPolicy($password) {
   if (strlen($password) < 6) throw new AuthException(T_('Password must be at least 6 characters'));
   //if ()
   
 }
 
 public function registerAccount($email, $pass, $userName = null, $language = null, $theme = null) {
    global $config;            
    
    $userName = ($userName === null) ? substr($email, 0, strpos($email,'@')) : $userName;
    
    $token = globalFunc::generateToken();
    
    $checkEmail = $this->db->getRepository('User')->findByEMail($email);
    if ($checkEmail != null) throw new AuthException(T_('User already registered'));
    
    $checkUserName = $this->db->getRepository('User')->findByUserName($userName);
    if ($checkUserName != null) throw new AuthException(T_('Display name already used'));
    
    $this->checkPasswordPolicy($pass);
    
    try {         
      
      $user = new User();
      $user->EMail = strtolower($email);
      $user->Password = globalFunc::hashPassword($pass,$email);
      $user->UserName = $userName;
      $user->isActive = false;
      $user->isRecovery = false;
      $user->Language = $this->db->find('Language', ($language === null) ? $config['Page']['Language'] : $language);
      $user->Style = $this->db->find('Style', ($theme === null) ? $config['Page']['Theme'] : $theme);
      $user->Token = $token;
      $user->LastAction = new DateTime('now');

      $this->db->persist($user);
      $this->db->flush();

      $mail = new PHPMailer(true);   
     
      $mail->CharSet  = "UTF-8";   
      $mail->From     = $config['Mail']['From'];
      $mail->FromName = $config['Mail']['From'];
      $mail->Subject  = ProjectName.' - '.T_('Account activation');
      
       
      $activationLink = globalFunc::getUrl().'activate.php?user='.$user->idUser.'&token='.$token;
       
      //$text = T_('-- activation email --');
      $text = T_('Welcome on %s
        
You or someone else used your e-mail address (%s) to create account on %s < %s >

To complete the registration process, you have to visit following address in next 24 hours

%s

If you don\'t, your data will be automaticly deleted by our system.');
       
      $mail->Body = sprintf($text, ProjectName, $email, ProjectName, globalFunc::getUrl() ,$activationLink);
              
      $mail->AddAddress($email);     
      $mail->Send();
      
      echo T_("To your email adress has been sent an activation e-mail. Please follow instructions within"); 
      
    } catch (DomainException $ex) {
      throw new AuthException($ex->getMessage());
    } catch (LengthException $ex) {
      throw new AuthException($ex->getMessage());
    } catch (UnexpectedValueException $ex) {
      throw new AuthException($ex->getMessage());
    } catch (phpmailerException $ex) {
      throw new AuthException($ex->getMessage());
    } catch (Exception $ex) {
      globalFunc::redirect(WebRoot.'index.php');
    }
    
  }


 private function setStorages($token, $lifetime) {
   $_SESSION[self::$tokenName] = $token; 
   
   if ($lifetime !== null)
     $this->Cookie->setExpiration($lifetime)->gall_persist = $token;
 }
 
 private function unsetStorages() {
   unset($_SESSION[self::$tokenName]);
   unset($_SESSION['albums']);
   
   $this->Cookie->setExpiration(null)->gall_persist = null;
   
   session_regenerate_id(true);
 }
  
 }

?>
